package com.chachazhan.logback.kestrel;

import net.rubyeye.xmemcached.MemcachedClient;
import net.rubyeye.xmemcached.MemcachedClientBuilder;
import net.rubyeye.xmemcached.XMemcachedClientBuilder;
import net.rubyeye.xmemcached.command.KestrelCommandFactory;
import net.rubyeye.xmemcached.impl.RoundRobinMemcachedSessionLocator;
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

/**
 * A kestrel wrapper. Underline is the xmemcached client.
 * 
 * @author mingqi.zhou
 *
 */
public class KestrelClient {

    private static Logger log = LoggerFactory.getLogger(KestrelClient.class);
    
    private MemcachedClient client;
    
    private final String ips;
    private final int poolSize;

    public KestrelClient(String ips) {
        this.ips = ips;
        this.poolSize = 5;
        init();
    }
    
    public KestrelClient(String ips, int poolSize) {
        this.ips = ips;
        this.poolSize = poolSize;
        init();
    }

    private void init() {
        if (StringUtils.isNotEmpty(ips)) {
            try {
                MemcachedClientBuilder builder = new XMemcachedClientBuilder(getAddresses(ips));
                builder.setCommandFactory(new KestrelCommandFactory());
                builder.setConnectionPoolSize(poolSize);
                // user round robin load balancer for kestrel, or it won't distribute object to 
                // different servers for same queue 
                builder.setSessionLocator(new RoundRobinMemcachedSessionLocator());
                client = builder.build();
                //client.setPrimitiveAsString(true);
            } catch (IOException e) {
                log.error("Failed to connect to kestrel servers...", e);
            }
        } else {
            log.error("Failed to init kestrel client. ");
        }
    }
    
    
    /**
     * Put an item into the queue.
     * @param queue
     * @param exp
     * @param value
     * @return
     */
    public boolean put(final String queue, final int exp, final Object value) {
        if (client == null) {
            return false;
        }
        try {
            return client.set(queue, exp, value);
        } catch (Exception e) {
            log.warn("Exception while putting in to kestrel queue." + e.getMessage());
            return false;
        } 
    }
    
    
    /**
     * Put an item into the queue with no expiration.
     * @param queue
     * @param value
     * @return
     */
    public boolean put(final String queue, final Object value) {
        return put(queue, 0, value);
    }
    
    
    /**
     * Get an item from the queue.
     * @param queue
     * @return null if no more items
     */
    public Object get(final String queue) {
        if (client == null) {
            return null;
        }
        try {
            return client.get(queue);
        } catch (Exception e) {
            log.warn("Exception while getting from kestrel queue." + e.getMessage());
            return null;
        } 
    }


    public long getQueueSize(String ips, String queueName) {
        if (client == null) {
            return 0;
        }
        try {
            if (StringUtils.contains(ips, ":")) {
                String host = StringUtils.substringBefore(ips, ":");
                String portNum = StringUtils.substringAfter(ips, ":");
                Map<String, String> map = client.stats(new InetSocketAddress(host, Integer.parseInt(portNum)));

                for (Map.Entry<String, String> e : map.entrySet()){
                    if (StringUtils.equals(e.getKey(), "queue_"+ queueName+"_items")) {
                        return Long.parseLong(e.getValue());
                    }
                }
            }
        } catch (Exception e) {
            log.warn("Exception while getting size from kestrel queue." + e.getMessage());
            return 0;
        }
        return 0;
    }
    
    
    /**
     * Shutdown the kestrel client.
     */
    public void shutdown() {
        try {
            if (client != null) {
                client.shutdown();
            }
        } catch (IOException e) { 
            log.warn("Failed to shutdown kestrel client...", e);
        }
    }
    
    
    private static List<InetSocketAddress> getAddresses(String s) {
        if (s == null) {
            throw new NullPointerException("Null host list");
        }
        if (s.trim().equals("")) {
            throw new IllegalArgumentException("No hosts in list:  ``" + s + "''");
        }
        ArrayList<InetSocketAddress> addrs = new ArrayList<InetSocketAddress>();

        for (String hoststuff : s.split(",")) {
            int finalColon = hoststuff.lastIndexOf(':');
            if (finalColon < 1) {
                throw new IllegalArgumentException("Invalid server ``" + hoststuff + "'' in list:  " + s);

            }
            String hostPart = hoststuff.substring(0, finalColon);
            String portNum = hoststuff.substring(finalColon + 1);

            addrs.add(new InetSocketAddress(hostPart, Integer.parseInt(portNum)));
        }
        assert !addrs.isEmpty() : "No addrs found";
        return addrs;
    }
}
